package com.xinli.pay.wxpay.controller;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.AuthTypeEnum;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.model.v3.*;
;
import com.xinli.business.domain.*;
import com.xinli.business.service.*;
import com.xinli.common.core.domain.AjaxResult;
import com.xinli.common.core.domain.entity.SysUser;
import com.xinli.common.exception.ServiceException;
import com.xinli.common.utils.DateUtils;
import com.xinli.common.utils.SecurityUtils;
import com.xinli.common.utils.StringUtils;
import com.xinli.pay.wxpay.OrderStatus;
import com.xinli.pay.wxpay.config.WxPayV3Bean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.*;

/**
*
*
* 微信支付
*
*/
@Controller
@RequestMapping("/v3")
public class WxPayV3Controller {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    private final static int OK = 200;

    @Resource
    WxPayV3Bean wxPayV3Bean;

    String serialNo;
    String platSerialNo;

    @Autowired
    private ITOrderService tOrderService;

    @Autowired
    private IFtMemberService ftMemberService;

//	@Autowired
//	private ITOrderService tOrderService;
//
//	@Autowired
//	private PayService payService;


    @Autowired
    private ILfNoticeService lfNoticeService;

    private String getSerialNumber() {
        if (StrUtil.isEmpty(serialNo)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(wxPayV3Bean.getCertPath());
            if (null != certificate) {
                serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
                // 提前两天检查证书是否有效
                boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayV3Bean.getMchId(), -2);
                log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
            }
//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }






    /**
     *
     *
     * 调取支付
     *
     */
    @RequestMapping("/jsApiPay")
    @ResponseBody
    public AjaxResult jsApiPay(@RequestBody OrderVO vo) {
        try {

            TOrder order = tOrderService.getOrderByOrderNo(vo.getOrderNo());

            FtMember user = ftMemberService.selectFtMemberByMemberId(SecurityUtils.getUserId());
            if(user==null){
                throw new ServiceException("用户不存在");
            }

//            String openId = member1.getOpenId();
//            if(StringUtils.isEmpty(openId)){
//                throw new ServiceException("获取微信参数失败");
//            }
            // 每次更新 支付订单号
            String outTradeNo = order.getOrderNo() + "_" + IdUtil.simpleUUID().substring(0, 10);
            order.setOutTradeNo(outTradeNo);
            tOrderService.updateById(order);
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setAppid(wxPayV3Bean.getAppId())
                    .setMchid(wxPayV3Bean.getMchId())
                    .setDescription("支付")
                    .setOut_trade_no(outTradeNo)
                    .setTime_expire(timeExpire)
                    .setAttach("微信系开发脚手架 https://gitee.com/javen205/TNWX")
                    .setNotify_url(wxPayV3Bean.getDomain().concat("/v3/payNotify"))
                    .setAmount(new Amount().setTotal(1))
                    .setPayer(new Payer().setOpenid(user.getOpenId()));

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.JS_API_PAY.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );
            log.info("统一下单响应 {}", response);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            if (response.getStatus() == OK && verifySignature) {
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayV3Bean.getAppId(), prepayId, wxPayV3Bean.getKeyPath());
                log.info("唤起支付参数:{}", map);
                return  AjaxResult.success(JSONUtil.toJsonStr(map));
            }
            return AjaxResult.success(response);
        } catch (Exception e) {
            log.error("系统异常", e);
            return  AjaxResult.error(e.getMessage());
        }
    }


    @Autowired
    private  MeetingReservationClient  meetingReservationClient;

    @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
    @ResponseBody
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> map = new HashMap<>(12);
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("支付通知密文 {}", result);

            // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());
            log.info("支付通知明文 {}", plainText);
            Gson gson = new Gson();
            if (StrUtil.isNotEmpty(plainText)) {
                HashMap hashMap = gson.fromJson(plainText, HashMap.class);
                String orderNo = (String) hashMap.get("out_trade_no");
                String orderNo_real = orderNo.split("_")[0];
                this.processOrder(orderNo_real);
                System.out.println("微信notify_url 验证成功开始处理业务");
                response.setStatus(200);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            log.error("系统异常", e);
        }
    }




    public void processOrder(String orderNo) throws GeneralSecurityException {
        log.info("处理订单");
        //解密报文
//        String plainText = decryptFromResource(bodyMap);
        //将明文转换成map
//        Gson gson = new Gson();
//        HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
        /*在对业务数据进行状态检查和处理之前，
        要采用数据锁进行并发控制，
        以避免函数重入造成的数据混乱*/
        //尝试获取锁：
        // 成功获取则立即返回true，获取失败则立即返回false。不必一直等待锁的释放
        String uuId = IdUtil.simpleUUID();
        //处理重复的通知
        //接口调用的幂等性：无论接口被调用多少次，产生的结果是一致的。
        String orderStatus = this.getOrderStatus(orderNo);
        if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
            return;
        }
        log.info("更新订单状态-----------开始");
        //更新订单状态
        QueryWrapper<TOrder> queryWrapperorder = new QueryWrapper<>();
        queryWrapperorder.eq("order_no", orderNo);
        List<TOrder> list = tOrderService.list(queryWrapperorder);
        if (!CollectionUtils.isEmpty(list)) {
            TOrder tOrder = list.get(0);
            tOrder.setOrderStatus(OrderStatus.SUCCESS.getType());
            tOrder.setFinallyTime(DateUtil.date());
            tOrder.setPayTime(DateUtil.date());
            tOrder.setIsPayed("1");
            tOrderService.updateTOrder(tOrder);
            log.info("更新订单状态-----------成功");
            log.info("发送消息-----------开始");
            LfNotice lfNotice =new LfNotice();
            lfNotice.setSenderId(Long.valueOf(tOrder.getUserId()));
            lfNotice.setReceiverId(Long.valueOf(tOrder.getZxsId()));
            lfNotice.setContentType("1");
            lfNotice.setInformation("您当前有一个预约需要处理");
            lfNotice.setType("4");
            lfNotice.setExtId(tOrder.getId());
            lfNoticeService.insertLfNotice(lfNotice);
            log.info("发送消息-----------结束");
            // 创建会议
            this.createMeet(tOrder);

        }


    }

    @Async
    public void createMeet(TOrder tOrder) {
        log.info("开始创建会议-----------开始====================");
        String  meetingtitle = "咨询预约";
        String startTime = DateUtil.format(tOrder.getStartDate(), "yyyy-MM-dd HH:mm:ss");
        String endTime = DateUtil.format(tOrder.getEndDate(), "yyyy-MM-dd HH:mm:ss");
        String zxsId = tOrder.getZxsId();
        String userId = tOrder.getUserId();
        FtMember member = ftMemberService.selectFtMemberByMemberId(Long.valueOf(zxsId));
        // 邀请人
        Inviter inviter = new Inviter(member.getNickName(), "", member.getPhone());

        FtMember user = ftMemberService.selectFtMemberByMemberId(Long.valueOf(userId));
        // 被邀请人
        Invitee invitee = new Invitee(user.getNickName(), "", user.getPhone());
        String meetInfo = meetingReservationClient.reserveMeeting(meetingtitle, startTime, endTime, inviter, invitee);
        log.info("创建会议-----------结束===================="+meetInfo);
        tOrder.setMeetInfo(meetInfo);
        tOrderService.updateTOrder(tOrder);
        log.info("更新订单会议信息-----------结束====================");
    }



    /**
     * 根据订单号获取订单状态
     *
     * @param orderNo
     * @return
     */
    public String getOrderStatus(String orderNo) {
        TOrder orderInfo = new TOrder();
        orderInfo.setOrderNo(orderNo);
        List<TOrder> tOrderInfos = tOrderService.selectTOrderList(orderInfo);
        if (CollectionUtils.isEmpty(tOrderInfos)) {
            return null;
        } else {
            return tOrderInfos.get(0).getOrderStatus();

        }


    }

//    private TOrder getNoPayOrderByUserId(String userId, String productId) {
//        TOrder info = new TOrder();
//        info.setOrderStatus(OrderStatus.NOTPAY.getType());
//        info.setUserId(userId);
//        info.setProductId(productId);
//        List<TOrder> tOrderInfos = tOrderService.selectTOrderList(info);
//        if (CollectionUtils.isEmpty(tOrderInfos)) {
//            return null;
//        } else {
//            return tOrderInfos.get(0);
//        }
//
//    }


    /**
     * @param outTradeNo
     * @Author:zhaoyongke
     * @Description:前端 定时查询 关闭二维码
     * @Date: 2024/2/7 14:10
     */
    @RequestMapping("/query")
    @ResponseBody
    public String query(@RequestParam String outTradeNo) {
        try {
            Map<String, String> params = new HashMap<>(16);
            params.put("mchid", wxPayV3Bean.getMchId());

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(params));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    String.format(BasePayApiEnum.ORDER_QUERY_BY_OUT_TRADE_NO.toString(), outTradeNo),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    params,
                    AuthTypeEnum.RSA.getCode()
            );
            log.info("查询响应 {}", response);
            if (response.getStatus() == OK) {
                // 根据证书序列号查询对应的证书来验证签名结果
                boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
                log.info("verifySignature: {}", verifySignature);
                return response.getBody();
            }
            return JSONUtil.toJsonStr(response);
        } catch (Exception e) {
            log.error("系统异常", e);
            return e.getMessage();
        }
    }


    @Autowired
    private ITOrderRefundService tOrderRefundService;



    @RequestMapping("/refund")
    @ResponseBody
    public AjaxResult refund(@RequestParam(required = false) String transactionId,
                         @RequestParam(required = false) String refoundId) {
        TOrderRefund orderRefund = tOrderRefundService.selectTOrderRefundById(refoundId);
        String orderNo = orderRefund.getOrderNo();
        String outRefundNo = PayKit.generateStr();
        log.info("商户退款单号: {}", outRefundNo);
        TOrder orderByOrderNo = tOrderService.getOrderByOrderNo(orderNo);
        try {
            String zxsId = orderByOrderNo.getZxsId();
//            int refundAmount = orderByOrderNo.getTotalPrice().multiply(new BigDecimal(100)).intValue();
            int refundAmount = 1;
            String outTradeNo = orderByOrderNo.getOutTradeNo();

            List<RefundGoodsDetail> list = new ArrayList<>();
            RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail()
                    .setMerchant_goods_id(zxsId)
                    .setGoods_name("咨询")
                    .setUnit_price(refundAmount)
                    .setRefund_amount(refundAmount)
                    .setRefund_quantity(1);
            list.add(refundGoodsDetail);

            RefundModel refundModel = new RefundModel()
                    .setOut_refund_no(outRefundNo)
                    .setReason("退款")
                    .setNotify_url(wxPayV3Bean.getDomain().concat("/v3/refundNotify"))
                    .setAmount(new RefundAmount().setRefund(refundAmount).setTotal(refundAmount).setCurrency("CNY"))
                    .setGoods_detail(list);

            if (StrUtil.isNotEmpty(transactionId)) {
                refundModel.setTransaction_id(transactionId);
            }
            if (StrUtil.isNotEmpty(outTradeNo)) {
                refundModel.setOut_trade_no(outTradeNo);
            }
            log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.REFUND.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(refundModel)
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            log.info("退款响应 {}", response);
            if (verifySignature) {

               // {"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":1,"payer_total":1,"refund":1,"refund_fee":0,"settlement_refund":1,"settlement_total":1,"total":1},"channel":"ORIGINAL","create_time":"2025-05-09T11:32:25+08:00","funds_account":"AVAILABLE","out_refund_no":"368989ec707b415282b311eb47488552","out_trade_no":"1920682225318367232_5f60de236d","promotion_detail":[],"refund_id":"50302103502025050964799353867","status":"PROCESSING","transaction_id":"4200002698202505099473460461","user_received_account":"支付用户零钱"}
                //{"code":"PARAM_ERROR","message":"transaction_id and out_trade_no both empty"}
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String code = jsonObject.getStr("code");
                if(!StringUtils.isEmpty(code)){
                    throw   new RuntimeException(body);
                }
                // 退款成功
                orderRefund.setRefundStatus("1");
                // 退款时间
                orderRefund.setRefundTime(DateUtils.getNowDate());
                tOrderRefundService.updateById(orderRefund);
                // （是否对款）已经退款
                orderByOrderNo.setIsRefund("1");
                tOrderService.updateById(orderByOrderNo);

                return  AjaxResult.success(body);
            }
        } catch (Exception e) {
            // 退款失败
            orderRefund.setRefundStatus("2");
            tOrderRefundService.updateById(orderRefund);
            log.error("系统异常", e);
            return  AjaxResult.error(e.getMessage());
        }
        return null;
    }


    public static void main(String[] args) {
        String totalAmount = "123.456";
        System.out.println((int) (Double.valueOf(totalAmount) * 100));
        System.out.println(new BigDecimal("0.01").multiply(new BigDecimal("0.1")));


    }
}
